/******************************************************************************************
Copyright(C), 2019-2020, Linyar.
文件名：L_MPP40_vi.c
作者：燕卫博	版本：V1.1	创建日期：2021.5.27
文件描述：
    对Hi3516DV300平台执行MPP-VI初始化。
版本信息：
    9Hv2，个人。
历史修改记录：
1. 2021-5-27:V1.1 燕卫博
从STv9-t拷贝更改。

2. 2021-8-11:V1.0 燕卫博
从9Hv1拷贝，增加对GC2053(DEV0-lane0+lane2)单路支持。
******************************************************************************************/
//                                              ↓
//+------------------------------------------------------------------------------------------+
//|                                          文件说明
//+------------------------------------------------------------------------------------------+
//|      VI(Video Input)即视频输入模块，用于对接不同的视频输入源，如MIPI Rx(mipi/LVDS/HISPI)
//|  SLVS-EC、BT.1120、BT.656、BT.601、DC等视频接口。——《参考》P194
//|      本文件中只针对荣品的Hi3516DV300+IMX307做单路VI(VIDev0-PIPE0-PhyChn0)适配。
//+------------------------------------------------------------------------------------------+
//|  编写参考《HiMPP V4.0媒体处理软件开发参考(2019-8-1)》（简称《参考》）。
//+------------------------------------------------------------------------------------------+
//|                                         头文件包含
//+------------------------------------------------------------------------------------------+
/*|*/  #include "L_MPP40_vi.h"
/*|*/  #include "L_MPP40_debug.h"
//+------------------------------------------------------------------------------------------+
//|                                         参数宏定义
//+------------------------------------------------------------------------------------------+
/*|*/  #define MIPI_DEV_NODE "/dev/hi_mipi"
/*|*/  #define SENSOR_FRAMERATE 30//传感器帧率，用于ISP控制设置
//+------------------------------------------------------------------------------------------+
//|                                          函数声明
//+------------------------------------------------------------------------------------------+
/*|*/  void L_StopISP(void);
/*|*/  HI_S32 ISP_Sensor_Regiter_callback(ISP_DEV IspDev, HI_U32 u32SnsId);
/*|*/  HI_S32 ISP_Sensor_UnRegiter_callback(ISP_DEV IspDev);
/*|*/  HI_S32 ISP_BindSns(ISP_DEV IspDev, HI_U32 u32SnsId, HI_S8 s8SnsDev);
//+------------------------------------------------------------------------------------------+
//|                                          全局变量
//+------------------------------------------------------------------------------------------+
//|    ISP线程的句柄
/*|*/  static pthread_t g_IspPid  =0;
/*|*/  static const VI_DEV ViDev  =0;
/*|*/  static const VI_PIPE ViPipe=0;
/*|*/  static const VI_CHN ViChn  =0;
//|
/*|*/  VI_PARA_S stVI_para_default=
/*|*/  {
/*|*/      .enAUTOExposure=HI_TRUE,
/*|*/      .exposuretime=0x4000,//默认值0x4000
/*|*/  };
//|
//|    需要引出供调用程序使用的配置项
/*|*/  VI_PARA_S *pstVI_para=&stVI_para_default;
//|
//|    ----------------------------------------
//|    检查SENSOR宏定义
//|    ----------------------------------------
/*|*/#if (!defined SENSOR_IMX307)&&(!defined SENSOR_GC2053)
/*|*/  #error No defined SENEOR_TYPE
/*|*/#endif
//|
/*|*/#if (defined SENSOR_IMX307)&&(defined SENSOR_GC2053)
/*|*/  #error Redefined SENEOR_TYPE
/*|*/#endif
//|
//|    ----------------------------------------
//|    VI设备的属性结构体
//|    ----------------------------------------
/*|*/#ifdef SENSOR_IMX307
/*|*/  static VI_DEV_ATTR_S DEV_ATTR_IMX307_2M_BASE=
/*|*/  {
/*|*/      VI_MODE_MIPI,
/*|*/      VI_WORK_MODE_1Multiplex,
/*|*/      {0xFFF00000, 0x0},
/*|*/      VI_SCAN_PROGRESSIVE,
/*|*/      {-1, -1, -1, -1},
/*|*/      VI_DATA_SEQ_YUYV,
//|
/*|*/      {
/*|*/          /* port_vsync   port_vsync_neg  port_hsync  port_hsync_neg */
/*|*/          VI_VSYNC_PULSE, VI_VSYNC_NEG_LOW, VI_HSYNC_VALID_SINGNAL, VI_HSYNC_NEG_HIGH, VI_VSYNC_VALID_SINGAL, VI_VSYNC_VALID_NEG_HIGH,
//|            ----------------------------------------
//|            此成员适用D1连接，在MIPI下无效
//|            ----------------------------------------
/*|*/          {
/*|*/              /* hsync_hfb    hsync_act    hsync_hhb */
/*|*/              0, 1280,0,
/*|*/              /* vsync0_vhb vsync0_act vsync0_hhb */
/*|*/              0, 720,0,
/*|*/              /* vsync1_vhb vsync1_act vsync1_hhb */
/*|*/              0, 0, 0
/*|*/          }
/*|*/      },
/*|*/      VI_DATA_TYPE_RGB,
/*|*/      HI_FALSE,
/*|*/      {1920, 1080},
/*|*/      {
/*|*/          {
/*|*/              {1920 ,1080},
/*|*/
/*|*/          },
/*|*/          {
/*|*/              VI_REPHASE_MODE_NONE,
/*|*/              VI_REPHASE_MODE_NONE
/*|*/          }
/*|*/      },
/*|*/      {
/*|*/          WDR_MODE_NONE,
/*|*/          1080
/*|*/      },
/*|*/      DATA_RATE_X1
/*|*/  };
/*|*/#endif
/*|*/#ifdef SENSOR_GC2053
/*|*/  static VI_DEV_ATTR_S DEV_ATTR_GC2053_2M_BASE =
/*|*/  {
/*|*/      VI_MODE_MIPI,
/*|*/      VI_WORK_MODE_1Multiplex,
/*|*/      {0xFFC00000,    0x0},
/*|*/      VI_SCAN_PROGRESSIVE,
/*|*/      { -1, -1, -1, -1},
/*|*/      VI_DATA_SEQ_YUYV,
/*|*/
/*|*/      {
/*|*/          /*port_vsync   port_vsync_neg     port_hsync        port_hsync_neg        */
/*|*/          VI_VSYNC_PULSE, VI_VSYNC_NEG_LOW, VI_HSYNC_VALID_SINGNAL, VI_HSYNC_NEG_HIGH, VI_VSYNC_VALID_SINGAL, VI_VSYNC_VALID_NEG_HIGH,
/*|*/
/*|*/          /*hsync_hfb    hsync_act    hsync_hhb*/
/*|*/          {
/*|*/              0,            1280,        0,
/*|*/              /*vsync0_vhb vsync0_act vsync0_hhb*/
/*|*/              0,            720,        0,
/*|*/              /*vsync1_vhb vsync1_act vsync1_hhb*/
/*|*/              0,            0,            0
/*|*/          }
/*|*/      },
/*|*/      VI_DATA_TYPE_RGB,
/*|*/      HI_FALSE,
/*|*/      {1920 , 1080},
/*|*/      {
/*|*/          {
/*|*/              {1920 , 1080},
/*|*/          },
/*|*/          {
/*|*/              VI_REPHASE_MODE_NONE,
/*|*/              VI_REPHASE_MODE_NONE
/*|*/          }
/*|*/      },
/*|*/      {
/*|*/          WDR_MODE_NONE,
/*|*/          1080
/*|*/      },
/*|*/      DATA_RATE_X1
/*|*/  };
/*|*/#endif
//|
//|    ----------------------------------------
//|    VI的PIPE属性结构体
//|    ----------------------------------------
/*|*/#ifdef SENSOR_IMX307
/*|*/static VI_PIPE_ATTR_S PIPE_ATTR_1920x1080_RAW12_420_3DNR_RFR=
/*|*/{
/*|*/    VI_PIPE_BYPASS_NONE, HI_FALSE, HI_FALSE,
/*|*/    1920, 1080,
/*|*/    PIXEL_FORMAT_RGB_BAYER_12BPP,
/*|*/    COMPRESS_MODE_NONE,
/*|*/    DATA_BITWIDTH_12,
/*|*/    HI_FALSE,
/*|*/    {
/*|*/        PIXEL_FORMAT_YVU_SEMIPLANAR_420,
/*|*/        DATA_BITWIDTH_8,
/*|*/        VI_NR_REF_FROM_RFR,
/*|*/        COMPRESS_MODE_NONE
/*|*/    },
/*|*/    HI_FALSE,
/*|*/    {-1, -1}
/*|*/};
/*|*/#endif
/*|*/#ifdef SENSOR_GC2053
/*|*/  static VI_PIPE_ATTR_S PIPE_ATTR_1920x1080_RAW10_420_3DNR_RFR =
/*|*/  {
/*|*/      VI_PIPE_BYPASS_NONE, HI_FALSE, HI_FALSE,
/*|*/      1920, 1080,
/*|*/      PIXEL_FORMAT_RGB_BAYER_10BPP,
/*|*/      COMPRESS_MODE_NONE,
/*|*/      DATA_BITWIDTH_10,
/*|*/      HI_TRUE,
/*|*/      {
/*|*/          PIXEL_FORMAT_YVU_SEMIPLANAR_420,
/*|*/          DATA_BITWIDTH_8,
/*|*/          VI_NR_REF_FROM_RFR,
/*|*/          COMPRESS_MODE_NONE
/*|*/      },
/*|*/      HI_FALSE,
/*|*/      { -1, -1}
/*|*/  };
/*|*/#endif
//|
//|    ----------------------------------------
//|    VI的通道属性结构体
//|    ----------------------------------------
/*|*/#if (defined SENSOR_IMX307)||(defined SENSOR_GC2053)
/*|*/  static VI_CHN_ATTR_S CHN_ATTR_1920x1080_420_SDR8_LINEAR=
/*|*/  {
/*|*/      {1920, 1080},
/*|*/      PIXEL_FORMAT_YVU_SEMIPLANAR_420,
/*|*/      DYNAMIC_RANGE_SDR8,
/*|*/      VIDEO_FORMAT_LINEAR,
/*|*/      COMPRESS_MODE_NONE,
/*|*/      0,0,
/*|*/      0,
/*|*/      {-1, -1}
/*|*/  };
/*|*/#endif
//|
//|    ----------------------------------------
//|    ISP公共属性结构体
//|    ----------------------------------------
/*|*/#if (defined SENSOR_IMX307)||(defined SENSOR_GC2053)
/*|*/  static ISP_PUB_ATTR_S ISP_PUB_ATTR_MIPI_2M_30FPS=
/*|*/  {
/*|*/      {0, 0, 1920, 1080},
/*|*/      {1920, 1080},
/*|*/      30,
/*|*/      BAYER_RGGB,
/*|*/      WDR_MODE_NONE,
/*|*/      0,
/*|*/  };
/*|*/#endif
//|
//|    ----------------------------------------
//|              MIPI设备的属性结构体
//|    Hi3516EV200只有两条LANE，分别为lane 0和
//|    lane 2，所以lane _id配置为{0,2,-1,-1}。
//|    ——《MIPI使用指南》P65、P54~P55
//|    ----------------------------------------
/*|*/#ifdef SENSOR_IMX307
/*|*/  static combo_dev_attr_t MIPI_2lane_CHN0_SENSOR_IMX307_12BIT_2M_NOWDR_ATTR =
/*|*/  {
/*|*/      .devno = 0,
/*|*/      .input_mode = INPUT_MODE_MIPI,
/*|*/      .data_rate  = MIPI_DATA_RATE_X1,
/*|*/      .img_rect   = {0, 0, 1920, 1080},
/*|*/      {
/*|*/          .mipi_attr =
/*|*/          {
/*|*/              DATA_TYPE_RAW_12BIT,
/*|*/              HI_MIPI_WDR_MODE_NONE,
/*|*/              {0, 2, -1, -1}
/*|*/          }
/*|*/      }
/*|*/  };
/*|*/#endif
/*|*/#ifdef SENSOR_GC2053
/*|*/  static combo_dev_attr_t MIPI_2lane_CHN0_SENSOR_GC2053_10BIT_2M_NOWDR_ATTR =
/*|*/  {
/*|*/      .devno = 0,
/*|*/      .input_mode = INPUT_MODE_MIPI,
/*|*/      .data_rate = MIPI_DATA_RATE_X1,
/*|*/      .img_rect = {0, 0, 1920, 1080},
/*|*/      {
/*|*/          .mipi_attr =
/*|*/          {
/*|*/              DATA_TYPE_RAW_10BIT,
/*|*/              HI_MIPI_WDR_MODE_NONE,
/*|*/              {0, 2, -1, -1}
/*|*/          }
/*|*/      }
/*|*/  };
/*|*/#endif
//+------------------------------------------------------------------------------------------+

//+------------------------------------------------------------------------------------------+
//|  函数名称：SAMPLE_COMM_ISP_Thread
//|  功能描述：ISP线程。
//|  参数说明：略
//|  返回值说明：无
//|  备注：从SAMPLE简单修改得到。
//+------------------------------------------------------------------------------------------+
static void *SAMPLE_COMM_ISP_Thread(void *param)
{
    HI_S32 s32Ret;
    ISP_DEV IspDev;
    HI_CHAR szThreadName[20];

    IspDev = (ISP_DEV)param;
    snprintf(szThreadName, 20, "ISP%d_RUN", IspDev);
    prctl(PR_SET_NAME, szThreadName, 0, 0, 0);

    //----------------------------------------
    //HI_MPI_ISP_Run是阻塞接口。详细介绍见
    //《HiISP 开发参考(2019-7-10)》P39
    //----------------------------------------
    s32Ret = HI_MPI_ISP_Run(IspDev);
    if(s32Ret != HI_SUCCESS)LOGE("[MPP-VI-ERROR]SAMPLE_COMM_ISP_Thread:failed with %#x!\n", s32Ret);

    return NULL;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_SetMIPI
//|  功能描述：设置MIPI设备。
//|  参数说明：无
//|  返回值说明：成功返回0
//|  备注：
//+------------------------------------------------------------------------------------------+
static int L_SetMIPI(void)
{
    HI_S32 s32Ret = HI_SUCCESS;
    HI_S32 fd;
    combo_dev_t devno = 0;           //设备节点
    combo_dev_attr_t stcomboDevAttr; //设备节点属性
    sns_clk_source_t SnsDev = 0;     //传感器设备号
    //----------------------------------------
    //修改lane分布
    //----------------------------------------
    lane_divide_mode_t lane_divide_mode = LANE_DIVIDE_MODE_0;

    fd = open(MIPI_DEV_NODE, O_RDWR);
    do
    {
        if(fd < 0){LOGE("[MPP-VI-ERROR]L_SetMIPI:Open hi_mipi dev failed with %d!\n", fd);break;}
        //----------------------------------------
        //-设置MIPI模式-
        //----------------------------------------
        s32Ret = ioctl(fd, HI_MIPI_SET_HS_MODE, &lane_divide_mode);
        if(HI_SUCCESS != s32Ret){LOGE("[MPP-VI-ERROR]L_SetMIPI:HI_MIPI_SET_HS_MODE failed!\n");break;}

        //----------------------------------------
        //-使能MIPI时钟-
        //参考例程，若输入模式为INPUT_MODE_SLVS，
        //则此处设置项为HI_MIPI_ENABLE_SLVS_CLOCK
        //----------------------------------------
        s32Ret = ioctl(fd, HI_MIPI_ENABLE_MIPI_CLOCK, &devno);
        if(HI_SUCCESS != s32Ret){LOGE("[MPP-VI-ERROR]L_SetMIPI:MIPI_ENABLE_CLOCK %d failed!\n", devno);break;}

        //----------------------------------------
        //-重置MIPI-
        //----------------------------------------
        s32Ret = ioctl(fd, HI_MIPI_RESET_MIPI, &devno);
        if(HI_SUCCESS != s32Ret){LOGE("[MPP-VI-ERROR]L_SetMIPI:RESET_MIPI %d failed!\n", devno);break;}

        //----------------------------------------
        //-使能sensor时钟-
        //----------------------------------------
        s32Ret = ioctl(fd, HI_MIPI_ENABLE_SENSOR_CLOCK, &SnsDev);
        if(HI_SUCCESS != s32Ret){LOGE("[MPP-VI-ERROR]L_SetMIPI:HI_MIPI_ENABLE_SENSOR_CLOCK failed!\n");break;}

        //----------------------------------------
        //-sensor复位-
        //----------------------------------------
        s32Ret = ioctl(fd, HI_MIPI_RESET_SENSOR, &SnsDev);
        if(HI_SUCCESS != s32Ret){LOGE("[MPP-VI-ERROR]L_SetMIPI:HI_MIPI_RESET_SENSOR failed!\n");break;}

        //----------------------------------------
        //-设置MIPI属性-
        //对芯片型号的获取在例程中是用于下一步对MIPI
        //属性设置时，适配不同的初始化结构体。
        //本程序中指定了sensor，故获取芯片型号可以不
        //进行。
        //芯片型号定义在当前版本头文件autoconf.h中。
        //----------------------------------------
#ifdef __L_MPP40_DEBUG
        HI_U32 u32ChipId = 0;
        s32Ret = HI_MPI_SYS_GetChipId(&u32ChipId);
        if (s32Ret == HI_SUCCESS)LOGI("[MPP-VI-DEBUG]L_SetMIPI:Chip ID:%#x\n", u32ChipId);

#endif

        //----------------------------------------
        //-设置mipi属性-
        //在设置或更换stcomboDevAttr结构体的时候，
        //需要注意当前芯片支持的MIPIlane通道。
        //例如在3516EV200中仅支持lane0、lane2。
        //详细说明见相应结构体注释。
        //----------------------------------------
#ifdef SENSOR_IMX307
        hi_memcpy(&stcomboDevAttr,
                  sizeof(combo_dev_attr_t),
                  &MIPI_2lane_CHN0_SENSOR_IMX307_12BIT_2M_NOWDR_ATTR,
                  sizeof(combo_dev_attr_t));
#endif
#ifdef SENSOR_GC2053
        hi_memcpy(&stcomboDevAttr,
                  sizeof(combo_dev_attr_t),
                  &MIPI_2lane_CHN0_SENSOR_GC2053_10BIT_2M_NOWDR_ATTR,
                  sizeof(combo_dev_attr_t));
#endif
        stcomboDevAttr.devno = 0;
        s32Ret = ioctl(fd,HI_MIPI_SET_DEV_ATTR,&stcomboDevAttr);
        if(HI_SUCCESS != s32Ret){LOGE("[MPP-VI-ERROR]L_SetMIPI:MIPI_SET_DEV_ATTR failed with %#x!\n", s32Ret);break;}

        //----------------------------------------
        //-反重置MIPI-
        //参考例程，若输入模式为INPUT_MODE_SLVS，
        //则此处设置项为HI_MIPI_UNRESET_SLVS
        //----------------------------------------
        s32Ret = ioctl(fd, HI_MIPI_UNRESET_MIPI, &devno);
        if(HI_SUCCESS != s32Ret){LOGE("[MPP-VI-ERROR]L_SetMIPI:UNRESET_MIPI %d failed!\n",devno);break;}

        //----------------------------------------
        //-反重置传感器-
        //----------------------------------------
        s32Ret = ioctl(fd, HI_MIPI_UNRESET_SENSOR, &SnsDev);
        if(HI_SUCCESS != s32Ret){LOGE("[MPP-VI-ERROR]L_SetMIPI:HI_MIPI_UNRESET_SENSOR failed!\n");break;}

        close(fd);
        return 0;
    }while(0);

    close(fd);
    return -1;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_SetVIVPSSMode
//|  功能描述：设置VI-VPSS模式。
//|  参数说明：无
//|  返回值说明：成功返回0
//|  备注：只实现了一路PIPE(PIPE0)的配置。
//+------------------------------------------------------------------------------------------+
static int L_SetVIVPSSMode(void)
{
    HI_S32 s32Ret = HI_SUCCESS;
    VI_VPSS_MODE_S stVIVPSSMode;

    do
    {
        s32Ret = HI_MPI_SYS_GetVIVPSSMode(&stVIVPSSMode);
        if(HI_SUCCESS != s32Ret){LOGE("[MPP-VI-ERROR]L_SetVIVPSSMode:Get VI-VPSS mode Param failed with %#x!\n", s32Ret);break;}

        //----------------------------------------
        // 2021-6-8 修改
        //----------------------------------------
        stVIVPSSMode.aenMode[ViPipe] = VI_OFFLINE_VPSS_OFFLINE;

        s32Ret = HI_MPI_SYS_SetVIVPSSMode(&stVIVPSSMode);
        if(HI_SUCCESS != s32Ret){LOGE("[MPP-VI-ERROR]L_SetVIVPSSMode:Set VI-VPSS mode Param failed with %#x!\n", s32Ret);break;}

        return 0;
    }while(0);

    L_VI_DeInit();
    return -1;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_EnableVIDevice
//|  功能描述：使能VI设备。
//|  参数说明：无
//|  返回值说明：成功返回0
//|  备注：只实现了单VI的配置。
//+------------------------------------------------------------------------------------------+
static int L_EnableVIDevice(void)
{
    HI_S32 s32Ret = HI_SUCCESS;
    VI_DEV_ATTR_S stViDevAttr;
#ifdef SENSOR_IMX307
    hi_memcpy(&stViDevAttr, sizeof(VI_DEV_ATTR_S), &DEV_ATTR_IMX307_2M_BASE, sizeof(VI_DEV_ATTR_S));
    //stViDevAttr.stWDRAttr.enWDRMode=WDR_MODE_NONE;
#endif
#ifdef SENSOR_GC2053
    hi_memcpy(&stViDevAttr, sizeof(VI_DEV_ATTR_S), &DEV_ATTR_GC2053_2M_BASE, sizeof(VI_DEV_ATTR_S));
#endif

    do
    {
        s32Ret = HI_MPI_VI_SetDevAttr(ViDev,&stViDevAttr);
        if(s32Ret != HI_SUCCESS){LOGE("[MPP-VI-ERROR]L_EnableVIDevice:HI_MPI_VI_SetDevAttr failed with %#x!\n", s32Ret);break;}

        s32Ret = HI_MPI_VI_EnableDev(ViDev);
        if(s32Ret != HI_SUCCESS){LOGE("[MPP-VI-ERROR]L_EnableVIDevice:HI_MPI_VI_EnableDev failed with %#x!\n", s32Ret);break;}
        return 0;
    }while(0);

    L_VI_DeInit();
    return -1;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_VIbindPIPE
//|  功能描述：VI绑定PIPE。
//|  参数说明：无
//|  返回值说明：成功返回0
//|  备注：只绑定VI到一个PIPE上。
//+------------------------------------------------------------------------------------------+
static int L_VIbindPIPE(void)
{
    HI_S32 s32Ret = HI_SUCCESS;
    VI_DEV_BIND_PIPE_S stDevBindPipe = {0};
    //----------------------------------------
    //PipeId[VI_MAX_PIPE_NUM]表示该VI设备绑定
    //的PIPE号。
    //u32Num表示该VI设备绑定的PIPE数目，最大数
    //量为VI_MAX_PIPE_NUM。
    //——《参考》P391
    //VI_MAX_PIPE_NUM为VI-PIPE的最大个数，为最
    //大物理PIPE数目和最大虚拟PIPE数目之和。
    //——《参考》P342
    //在本版本《参考》的描述中，Hi3516DV300有
    //2个DEV、4个物理PIPE、0个虚拟PIPE、
    //1个物理通道、8个扩展通道。
    //——《参考》P196
    //----------------------------------------
    stDevBindPipe.u32Num    = 1;
    stDevBindPipe.PipeId[0] = ViPipe;
    s32Ret = HI_MPI_VI_SetDevBindPipe(ViDev, &stDevBindPipe);
    if(s32Ret != HI_SUCCESS)
    {
        LOGE("[MPP-VI-ERROR]L_VIbindPIPE:HI_MPI_VI_SetDevBindPipe failed with %#x!\n", s32Ret);
        L_VI_DeInit();
        return -1;
    }
    return 0;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_StartPIPE
//|  功能描述：设置PIPE属性并启动PIPE。
//|  参数说明：无
//|  返回值说明：成功返回0
//|  备注：
//+------------------------------------------------------------------------------------------+
static int L_StartPIPE(void)
{
    HI_S32 s32Ret = HI_SUCCESS;
    VI_PIPE_ATTR_S stPipeAttr;
#ifdef SENSOR_IMX307
    hi_memcpy(&stPipeAttr,sizeof(VI_PIPE_ATTR_S),&PIPE_ATTR_1920x1080_RAW12_420_3DNR_RFR,sizeof(VI_PIPE_ATTR_S));
#endif
#ifdef SENSOR_GC2053
    hi_memcpy(&stPipeAttr,sizeof(VI_PIPE_ATTR_S),&PIPE_ATTR_1920x1080_RAW10_420_3DNR_RFR,sizeof(VI_PIPE_ATTR_S));
#endif

    do
    {
        //----------------------------------------
        //在stPipeAttr中可以设置不运行ISP，这是为
        //自行设计ISP算法的程序设计的。
        //相关描述见《参考》P397
        //----------------------------------------
        s32Ret = HI_MPI_VI_CreatePipe(ViPipe, &stPipeAttr);
        if(s32Ret != HI_SUCCESS){LOGE("L_StartPIPE:HI_MPI_VI_CreatePipe failed with %#x!\n", s32Ret);break;}

        //----------------------------------------
        //在例程中接下来判断是否调用
        //HI_MPI_VI_SetPipeVCNumber，曾参考的
        //Hi3516DV300的MPP例程中是运行的，但在新的
        //例程中，该项调用未使用。
        //该函数说明见《参考》P266
        //----------------------------------------
        s32Ret = HI_MPI_VI_StartPipe(ViPipe);
        if(s32Ret != HI_SUCCESS)
        {
            HI_MPI_VI_DestroyPipe(ViPipe);
            LOGE("[MPP-VI-ERROR]L_StartPIPE:HI_MPI_VI_StartPipe failed with %#x!\n", s32Ret);break;
        }

        return 0;
    }while(0);

    L_VI_DeInit();
    return -1;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_EnableVIChannel
//|  功能描述：使能VI通道。
//|  参数说明：无
//|  返回值说明：成功返回0
//|  备注：此处使能的VI通道，是绑定的PIPE对应的物理通道。
//+------------------------------------------------------------------------------------------+
static int L_EnableVIChannel(void)
{
    HI_S32 s32Ret = HI_SUCCESS;
    VI_CHN_ATTR_S stChnAttr;
    memcpy_s(&stChnAttr, sizeof(VI_CHN_ATTR_S), &CHN_ATTR_1920x1080_420_SDR8_LINEAR, sizeof(VI_CHN_ATTR_S));
    stChnAttr.enCompressMode = COMPRESS_MODE_SEG;

    do
    {
        //----------------------------------------
        //这里例程进行了一次判断，若为WDR_MODE_NONE
        //则进行通道设置；若不为WDR_MODE_NONE，则
        //只在PIPE0进行通道属性设置？
        //----------------------------------------
        s32Ret = HI_MPI_VI_SetChnAttr(ViPipe, ViChn, &stChnAttr);
        if(s32Ret != HI_SUCCESS)
        {
            LOGE("[MPP-VI-ERROR]L_EnableVIChannel:HI_MPI_VI_SetChnAttr failed with %#x!\n", s32Ret);break;
        }

        //----------------------------------------
        //VI-VPSS工作模式不同，通道对使能的需求也
        //不同。
        //参考例程，在VI_OFFLINE_VPSS_OFFLINE、
        //            VI_ONLINE_VPSS_OFFLINE、
        //            VI_PARALLEL_VPSS_OFFLINE
        //情况下，需要手动使能通道。
        //在其它情况下，使能通道直接返回成功。
        //----------------------------------------
        s32Ret = HI_MPI_VI_EnableChn(ViPipe, ViChn);
        if(s32Ret != HI_SUCCESS){LOGE("[MPP-VI-ERROR]L_EnableVIChannel:HI_MPI_VI_EnableChn failed with %#x!\n", s32Ret);break;}

        return 0;
    }while(0);

    L_VI_DeInit();
    return -1;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_RunISP
//|  功能描述：启动ISP。
//|  参数说明：无
//|  返回值说明：成功返回0
//|  备注：此处启动的ISP，绑定了所选择的PIPE通道。
//+------------------------------------------------------------------------------------------+
static int L_RunISP(void)
{
    HI_S32 s32Ret   = HI_SUCCESS;
    HI_U32 u32SnsId = 0;
    ISP_PUB_ATTR_S stPubAttr;

    //----------------------------------------
    //更新ISP控制信息
    //此处的设置只作为调试时使用，根据帧率设置
    //了ISP信息刷新时间间隔。由于ISP和PIPE相关，
    //所以该参数设置也需要指定PIPE。
    //----------------------------------------
#ifdef __L_MPP40_DEBUG

    HI_U32 u32FrameRate = SENSOR_FRAMERATE;
    ISP_CTRL_PARAM_S stIspCtrlParam;
    s32Ret = HI_MPI_ISP_GetCtrlParam(ViPipe, &stIspCtrlParam);
    if(HI_SUCCESS != s32Ret){LOGE("[MPP-VI-ERROR]L_RunISP:HI_MPI_ISP_GetCtrlParam failed with %d!\n",s32Ret);return -1;}

    stIspCtrlParam.u32StatIntvl = u32FrameRate/30;
    if(stIspCtrlParam.u32StatIntvl == 0)stIspCtrlParam.u32StatIntvl = 1;

    s32Ret = HI_MPI_ISP_SetCtrlParam(ViPipe, &stIspCtrlParam);
    if(HI_SUCCESS != s32Ret)
    {
        LOGE("[MPP-VI-ERROR]L_RunISP:HI_MPI_ISP_SetCtrlParam failed with %d!\n",s32Ret);
        return -1;
    }

#endif

    //----------------------------------------
    //PIPE可以设置不运行ISP，这是为自行设计ISP
    //算法的程序设计的。此处运行。
    //----------------------------------------
    memcpy(&stPubAttr, &ISP_PUB_ATTR_MIPI_2M_30FPS, sizeof(ISP_PUB_ATTR_S));

    if((WDR_MODE_NONE == stPubAttr.enWDRMode)&&(ViPipe == 0))//线性模式需要PIPE，仅PIPE0支持
    {
        do
        {
            s32Ret = ISP_Sensor_Regiter_callback(ViPipe, u32SnsId);
            if(HI_SUCCESS != s32Ret){LOGE("[MPP-VI-ERROR]L_RunISP:register sensor %d to ISP %d failed with %#x!\n", u32SnsId, ViPipe, s32Ret);break;}

            //----------------------------------------
            //[注意]第3个参数(s32BusId)为传感器IIC设置
            //总线，和传感器连接情况有关。
            //----------------------------------------
            s32Ret = ISP_BindSns(ViPipe, u32SnsId, 0);
            if(HI_SUCCESS != s32Ret){LOGE("[MPP-VI-ERROR]L_RunISP:register sensor %d bus id %d failed with %#x!\n", u32SnsId, 0, s32Ret);break;}

            ALG_LIB_S stAeLib;
            stAeLib.s32Id = ViPipe;
            strncpy(stAeLib.acLibName, HI_AE_LIB_NAME, sizeof(HI_AE_LIB_NAME));
            s32Ret = HI_MPI_AE_Register(ViPipe,&stAeLib);
            if(HI_SUCCESS != s32Ret){LOGE("[MPP-VI-ERROR]L_RunISP:HI_MPI_AE_Register failed with %#x!\n", s32Ret);break;}

            ALG_LIB_S stAwbLib;
            stAwbLib.s32Id = ViPipe;
            strncpy(stAwbLib.acLibName, HI_AWB_LIB_NAME, sizeof(HI_AWB_LIB_NAME));
            s32Ret = HI_MPI_AWB_Register(ViPipe, &stAwbLib);
            if(HI_SUCCESS != s32Ret){LOGE("[MPP-VI-ERROR]L_RunISP:HI_MPI_AWB_Register failed with %#x!\n", s32Ret);break;}

            s32Ret = HI_MPI_ISP_MemInit(ViPipe);
            if(s32Ret != HI_SUCCESS){LOGE("[MPP-VI-ERROR]L_RunISP:HI_MPI_ISP_MemInit failed with %#x!\n", s32Ret);break;}

            s32Ret = HI_MPI_ISP_SetPubAttr(ViPipe,&stPubAttr);
            if(s32Ret != HI_SUCCESS){LOGE("[MPP-VI-ERROR]L_RunISP:HI_MPI_ISP_SetPubAttr failed with %#x!\n", s32Ret);break;}

            s32Ret = HI_MPI_ISP_Init(ViPipe);
            if(s32Ret != HI_SUCCESS){LOGE("[MPP-VI-ERROR]L_RunISP:HI_MPI_ISP_Init failed with %#x!\n", s32Ret);break;}

            /*自动/手动曝光设置*/
            do
            {
                if(pstVI_para->enAUTOExposure==HI_FALSE)
                {
                    ISP_EXPOSURE_ATTR_S ispExposureAttr;
                    s32Ret=HI_MPI_ISP_GetExposureAttr(ViPipe, &ispExposureAttr);
                    if(s32Ret!=HI_SUCCESS)
                    {
                        LOGE("[MPP-VI-ERROR]L_RunISP:HI_MPI_ISP_GetExposureAttr failed with %#x!",s32Ret);
                        LOGD("[MPP-VI-DEBUG]The program will ignore this setting.\n");
                        break;
                    }
                    ispExposureAttr.bByPass = HI_FALSE;//AE模块bypass功能使能
                    ispExposureAttr.enOpType = OP_TYPE_MANUAL;//自动或手动曝光开关
                    ispExposureAttr.u8AERunInterval=1;//AE算法运行间隔时间，取值1~255，默认为1
                    ispExposureAttr.stManual.enAGainOpType = OP_TYPE_MANUAL;
                    ispExposureAttr.stManual.enDGainOpType = OP_TYPE_MANUAL;
                    ispExposureAttr.stManual.enISPDGainOpType = OP_TYPE_MANUAL;
                    ispExposureAttr.stManual.enExpTimeOpType = OP_TYPE_MANUAL;
                    ispExposureAttr.stManual.u32AGain = 0x400;
                    ispExposureAttr.stManual.u32DGain = 0x400;
                    ispExposureAttr.stManual.u32ISPDGain = 0x400;
                    ispExposureAttr.stManual.u32ExpTime = pstVI_para->exposuretime;
                    s32Ret=HI_MPI_ISP_SetExposureAttr(ViPipe, &ispExposureAttr);
                    if(s32Ret!=HI_SUCCESS)
                    {
                        LOGE("[MPP-VI-ERROR]L_RunISP:HI_MPI_ISP_SetExposureAttr failed with %#x!\n",s32Ret);
                    }
                }
            }while(0);

            pthread_attr_t *pstAttr = NULL;
            ISP_DEV IspDev = ViPipe;
            s32Ret = pthread_create(&g_IspPid, pstAttr, SAMPLE_COMM_ISP_Thread, (HI_VOID *)IspDev);
            if(HI_SUCCESS != s32Ret)
            {
                LOGE("[MPP-VI-ERROR]L_RunISP:Run ISP failed with %#x!\n", s32Ret);
                if(NULL != pstAttr)pthread_attr_destroy(pstAttr);
                break;
            }

            return 0;
        }while(0);

        L_StopISP();
        L_VI_DeInit();
        return -1;
    }

    LOGE("[MPP-VI-ERROR]L_RunISP:Unknown error, please review code!\n");
    return -2;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_VI_Init
//|  功能描述：在MPP4.0上进行视频编码所需的VI初始化和ISP启动。
//|  参数说明：VI参数设置结构体地址
//|  返回值说明：成功返回0
//|  备注：若传入指针为NULL则使用默认配置。
//+------------------------------------------------------------------------------------------+
int L_VI_Init(VI_PARA_S *p)
{
    if(p != NULL)pstVI_para=p;

    do
    {
        if(L_SetMIPI()         != HI_SUCCESS)break;
        if(L_SetVIVPSSMode()   != HI_SUCCESS)break;
        if(L_EnableVIDevice()  != HI_SUCCESS)break;
        if(L_VIbindPIPE()      != HI_SUCCESS)break;
        if(L_StartPIPE()       != HI_SUCCESS)break;
        if(L_EnableVIChannel() != HI_SUCCESS)break;
        if(L_RunISP()          != HI_SUCCESS)break;

        return 0;
    }while(0);

    return -1;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_VI_DeInit
//|  功能描述：VI去初始化
//|  参数说明：无
//|  返回值说明：无
//|  备注：在本文件对VI的操作中，因为只有一个设备，所以只包括了一路PIPE的初始化，因此去初始化
//|        也是对一路而言的。
//+------------------------------------------------------------------------------------------+
void L_VI_DeInit(void)
{
    //----------------------------------------
    //通道禁用要返回成功，必须满足：
    //1.PIPE已经创建
    //2.物理通道绑定的扩展通道已经全部禁用
    //——《参考》P278
    //----------------------------------------
    HI_S32 s32Ret = HI_SUCCESS;
    s32Ret = HI_MPI_VI_DisableChn(ViPipe, ViChn);
    if(s32Ret != HI_SUCCESS)
    {
        LOGE("[MPP-VI-ERROR]L_VI_unInit:HI_MPI_VI_DisableChn failed with %#x!\n", s32Ret);
    }
    //----------------------------------------
    //V这些函数本身具有返回值，但是在去初始化时
    //即便返回错误的值，也用不到。故忽略。
    //----------------------------------------
    HI_MPI_VI_StopPipe(ViPipe);
    HI_MPI_VI_DestroyPipe(ViPipe);
    HI_MPI_VI_DisableDev(ViDev);
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_StopISP
//|  功能描述：ISP停止运行
//|  参数说明：无
//|  返回值说明：无
//|  备注：在本文件对VI的操作中，因为只有一个设备，所以只包括了一路PIPE的初始化，因此去初始化
//|        也是对一路而言的。
//+------------------------------------------------------------------------------------------+
void L_StopISP(void)
{
    if(g_IspPid)
    {
        HI_MPI_ISP_Exit(ViPipe);
        pthread_join(g_IspPid,NULL);

        ALG_LIB_S stAwbLib;
        stAwbLib.s32Id = ViPipe;
        strncpy(stAwbLib.acLibName, HI_AWB_LIB_NAME, sizeof(HI_AWB_LIB_NAME));
        HI_MPI_AWB_UnRegister(ViPipe, &stAwbLib);

        ALG_LIB_S stAeLib;
        stAeLib.s32Id=ViPipe;
        strncpy(stAeLib.acLibName, HI_AE_LIB_NAME, sizeof(HI_AE_LIB_NAME));
        HI_MPI_AE_UnRegister(ViPipe, &stAeLib);

        ISP_Sensor_UnRegiter_callback(ViPipe);

        g_IspPid = 0;
    }
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：ISP_Sensor_Regiter_callback
//|  功能描述：ISP中sensor注册函数。
//|  参数说明：略
//|  返回值说明：成功返回0
//|  备注：从SAMPLE简单修改得到。
//+------------------------------------------------------------------------------------------+
HI_S32 ISP_Sensor_Regiter_callback(ISP_DEV IspDev,HI_U32 u32SnsId)
{
    ALG_LIB_S stAeLib;
    ALG_LIB_S stAwbLib;
    const ISP_SNS_OBJ_S *pstSnsObj;
    HI_S32 s32Ret=-1;

#ifdef SENSOR_IMX307
    pstSnsObj = &stSnsImx307_2l_Obj;
#endif
#ifdef SENSOR_GC2053
    pstSnsObj = &stSnsGc2053Obj;
#endif
    if(HI_NULL==pstSnsObj)
    {
        LOGE("[MPP-VI-ERROR]ISP_Sensor_Regiter_callback:sensor %d not exist!\n",u32SnsId);
        return HI_FAILURE;
    }

    stAeLib.s32Id=IspDev;
    stAwbLib.s32Id=IspDev;
    strncpy(stAeLib.acLibName,HI_AE_LIB_NAME,sizeof(HI_AE_LIB_NAME));
    strncpy(stAwbLib.acLibName,HI_AWB_LIB_NAME,sizeof(HI_AWB_LIB_NAME));
    //  strncpy(stAfLib.acLibName,HI_AF_LIB_NAME,sizeof(HI_AF_LIB_NAME));//AF库未实现

    if(pstSnsObj->pfnRegisterCallback!=HI_NULL)
    {
        s32Ret=pstSnsObj->pfnRegisterCallback(IspDev,&stAeLib,&stAwbLib);
        if(s32Ret!=HI_SUCCESS)
        {
            LOGE("[MPP-VI-ERROR]ISP_Sensor_Regiter_callback:failed with %#x!\n",s32Ret);
            return s32Ret;
        }
    }
    else
    {
        LOGE("[MPP-VI-ERROR]ISP_Sensor_Regiter_callback:failed with null!\n");
    }

    return HI_SUCCESS;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：ISP_Sensor_UnRegiter_callback
//|  功能描述：ISP中sensor反注册函数。
//|  参数说明：略
//|  返回值说明：无
//|  备注：从SAMPLE简单修改得到。
//+------------------------------------------------------------------------------------------+
HI_S32 ISP_Sensor_UnRegiter_callback(ISP_DEV IspDev)
{
    ALG_LIB_S stAeLib;
    ALG_LIB_S stAwbLib;
    const ISP_SNS_OBJ_S *pstSnsObj;
    HI_S32    s32Ret=-1;

#ifdef SENSOR_IMX307
    pstSnsObj = &stSnsImx307_2l_Obj;
#endif
#ifdef SENSOR_GC2053
    pstSnsObj = &stSnsGc2053Obj;
#endif
    if(HI_NULL==pstSnsObj)
    {
        return HI_FAILURE;
    }

    stAeLib.s32Id=IspDev;
    stAwbLib.s32Id=IspDev;
    strncpy(stAeLib.acLibName,HI_AE_LIB_NAME,sizeof(HI_AE_LIB_NAME));
    strncpy(stAwbLib.acLibName,HI_AWB_LIB_NAME,sizeof(HI_AWB_LIB_NAME));
    //strncpy(stAfLib.acLibName,HI_AF_LIB_NAME,sizeof(HI_AF_LIB_NAME));//AF库未实现

    if(pstSnsObj->pfnUnRegisterCallback!=HI_NULL)
    {
        s32Ret=pstSnsObj->pfnUnRegisterCallback(IspDev,&stAeLib,&stAwbLib);

        if(s32Ret!=HI_SUCCESS)
        {
            LOGE("[MPP-VI-ERROR]ISP_Sensor_UnRegiter_callback:sensor_unregister_callback failed with %#x!\n",s32Ret);
            return s32Ret;
        }
    }
    else
    {
        LOGE("[MPP-VI-ERROR]ISP_Sensor_UnRegiter_callback:sensor_unregister_callback failed with null!\n");
    }

    return HI_SUCCESS;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：ISP_BindSns
//|  功能描述：ISP和sensor绑定函数。
//|  参数说明：略
//|  返回值说明：成功返回0
//|  备注：从SAMPLE简单修改得到。
//+------------------------------------------------------------------------------------------+
HI_S32 ISP_BindSns(ISP_DEV IspDev, HI_U32 u32SnsId, HI_S8 s8SnsDev)
{
    ISP_SNS_COMMBUS_U uSnsBusInfo;
    ISP_SNS_TYPE_E enBusType;
    const ISP_SNS_OBJ_S *pstSnsObj;
    HI_S32 s32Ret=HI_SUCCESS;

#ifdef SENSOR_IMX307
    pstSnsObj = &stSnsImx307_2l_Obj;
#endif
#ifdef SENSOR_GC2053
    pstSnsObj = &stSnsGc2053Obj;
#endif
    if(HI_NULL==pstSnsObj)
    {
        LOGE("[MPP-VI-ERROR]ISP_BindSns:sensor %d not exist!\n",u32SnsId);
        return HI_FAILURE;
    }

    enBusType=ISP_SNS_I2C_TYPE;//仅I2C
    if(ISP_SNS_I2C_TYPE==enBusType)
    {
        uSnsBusInfo.s8I2cDev=s8SnsDev;
    }
    else
    {
        uSnsBusInfo.s8SspDev.bit4SspDev=s8SnsDev;
        uSnsBusInfo.s8SspDev.bit4SspCs=0;
    }

    s32Ret=pstSnsObj->pfnSetBusInfo(IspDev,uSnsBusInfo);
    if(s32Ret!=HI_SUCCESS)
    {
        LOGE("[MPP-VI-ERROR]ISP_BindSns:set sensor bus info failed with %#x!\n",s32Ret);
        return s32Ret;
    }

    return s32Ret;
}
